#ifndef __M_ROOM_H__
#define __M_ROOM_H__
#include "util.hpp"
#include "onlineuser.hpp"
#include "db.hpp"
#define CHESS_ROW 15
#define CHESS_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum
{
    GAMESTART,
    GAMEOVER
} room_statu;

/*
    room类 将来就是 创建一个房间后有我们的服务器给我们一个房间号 和此时下棋的双方uid
    以及管理的用户信息
    接着处理请求 返回应答
    判断应答中如果返回true 并且此时的Winner_id不为0 去调用我们的退出房间接口

*/
class room
{
private:
    uint64_t _room_id;
    room_statu _room_statu;
    int _player_num;
    uint64_t _black_id;
    uint64_t _white_id;
    std::vector<std::vector<int>> _board;
    user_table *_user;   // 一个用户的属性
    online *_onlineuser; // 一个维护用户连接的句柄 里面管理了所有的在线用户

    // 在同一条线上相反的两个方向上 棋子的坐标的偏移量互为相反数
    bool help(int row, int col, int row_off, int col_off, int color)
    {
        int count = 1;
        int currow = row + row_off;
        int curcol = col + col_off;
        while (currow >= 0 && currow < CHESS_ROW && curcol >= 0 && curcol < CHESS_COL && _board[currow][curcol] == color)
        {
            count++;
            currow += row_off;
            curcol += col_off;
        }
        currow = row - row_off;
        curcol = col - col_off;
        while (currow >= 0 && currow < CHESS_ROW && curcol >= 0 && curcol < CHESS_COL && _board[currow][curcol] == color)
        {
            count++;
            currow -= row_off;
            curcol -= col_off;
        }
        return count >= 5;
    }
    uint64_t check_iswin(int row, int col, int color)
    {
        // 有获胜的返回获胜的id 没有人获胜返回0
        if (help(row, col, 0, 1, color) || help(row, col, -1, 0, color) || help(row, col, -1, 1, color) || help(row, col, -1, -1, color))
        {
            // 这里也可以加上积分更新逻辑
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }

public:
    room(uint64_t room_id, user_table *user, online *onlinemannger) : _room_id(room_id), _room_statu(GAMESTART), _player_num(0),
                                                                      _board(CHESS_ROW, std::vector<int>(CHESS_COL, 0)), _user(user),
                                                                      _onlineuser(onlinemannger)
    {
        DBG_LOG("%lu room create success", _room_id);
    }
    ~room()
    {
        // if(_player_num==0)
        DBG_LOG("%lu room destory success", _room_id);
    }
    room_statu statu() { return _room_statu; }
    int player_count() { return _player_num; }

    void set_whiteuser_id(uint64_t id)
    {
        _white_id = id;
        _player_num++;
    };
    void set_blackuser_id(uint64_t id)
    {
        _black_id = id;
        _player_num++;
    };
    void set_room_id(uint64_t id) { _room_id = id; };

    uint64_t get_whiteuser_id() { return _white_id; };
    uint64_t get_blackuser_id() { return _black_id; };
    uint64_t get_room_id() { return _room_id; };

    // 动作1:处理下棋
    Json::Value handle_putchess(Json::Value req)
    {
        // 判断当前的客户端用户是否断开了连接
        Json::Value json_resp = req; /*如果失败的话直接设置失败理由即可*/
        uint64_t req_uid = req["uid"].asUInt64();
        if (_onlineuser->is_in_game_room(_white_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "运气真好！白棋掉线，不战而胜！";
            json_resp["winner"] = (Json::UInt64)_black_id;
            return json_resp;
        }
        if (_onlineuser->is_in_game_room(_black_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "运气真好！黑棋掉线，不战而胜！";
            json_resp["winner"] = (Json::UInt64)_white_id;
            return json_resp;
        }

        int put_row = req["row"].asInt();
        int put_col = req["col"].asInt();
        if (_board[put_row][put_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已经有了其他棋子！";
            return json_resp;
        }
        int cur_chess_color = req_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[put_row][put_col] = cur_chess_color;
        // 判断谁获胜 没有人获胜 返回false 接着下棋
        uint64_t winner_uid = check_iswin(put_row, put_col, cur_chess_color);
        if (winner_uid != 0)
        {
            // 此时可以跟新我们的积分了啊
            json_resp["reason"] = "五星连珠，战无敌！";
            _user->win(winner_uid);
            _user->lose(_black_id==winner_uid?_white_id:_black_id);
            _room_statu=GAMEOVER;
        }

        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_uid; // 浏览器收到应答后判断获胜的是不是为0如果不是客户端继续下棋逻辑
        
        return json_resp;
    }
    // 动作2:处理我们的聊天
    Json::Value handle_chat(Json::Value req)
    {
        Json::Value json_resp = req;
        // 检测消息中是否包含敏感词
        std::string msg = req["message"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "消息中包含敏感词，不能发送！";
            return json_resp;
        }
        // 广播消息---返回消息
        json_resp["result"] = true;
        return json_resp;
    }
    // 该函数房间中有人退出
    void handle_exit(uint64_t uid)
    {
        // 如果是下棋中退出，则对方胜利，否则下棋结束了退出，则是正常退出
        Json::Value json_resp;
        if (_room_statu == GAMESTART)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，不战而胜！";
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _user->win(winner_id);
            _user->lose(loser_id);
            _room_statu = GAMEOVER;
            handle_broad(json_resp);
        }
        // 房间中玩家数量--
        _player_num--;
        return;
    }
    // 总的请求  不论 下棋还是聊天 包括请求类型字段 以及请求房间id 以及当前操作用户的uid
    void handle_request(Json::Value req)
    {
        std::string req_style = req["optype"].asString();
        uint64_t reps_user_id = req["uid"].asUInt64();
        uint64_t reps_room_id = req["room_id"].asUInt64();
        // 当前请求房间id 和我们的实例化对象id不匹配 直接构建应答 进行广播
        Json::Value reps;
        if (reps_room_id != _room_id)
        {
            reps["optype"] = req_style;
            reps["result"] = false;
            reps["reason"] = "请求房间id 和我们的当前房间id不匹配";
            handle_broad(reps);
            return;
        }
        if (req_style == "put_chess")
        {
            reps = handle_putchess(req);
        }
        else if (req_style == "chat")
        {
            reps = handle_chat(req);
        }
        else
        {
            reps["optype"] = req_style;
            reps["result"] = false;
            reps["reason"] = "未知请求";
            handle_broad(reps);
            return;
        }
        // 处理完请求
        handle_broad(reps);
        // 调试打印
        std::string body;
        json_util::serialize(reps, body);
        DBG_LOG("房间-广播动作: %s", body.c_str());
        return;
    }

    // 广播 我们服务器收到请求 发送给房间内的所有的客户端
    void handle_broad(Json::Value reps)
    {
        std::string body;
        json_util::serialize(reps, body);
        // 获取我们的连接
        webserve_t::connection_ptr white_conn = _onlineuser->get_conn_from_room(_white_id);
        if (white_conn != nullptr)
        {
            white_conn->send(body);
        }
        else
        {
            DBG_LOG("房间-白棋玩家连接获取失败");
        }
        webserve_t::connection_ptr black_conn = _onlineuser->get_conn_from_room(_black_id);
        if (black_conn != nullptr)
        {
            black_conn->send(body);
        }
        else
        {
            DBG_LOG("房间-黑棋玩家连接获取失败");
        }
    }
};

/*
    改类到时候我们会创建一个对象 其中管理我们的所有的房间信息
*/
using room_ptr = std::shared_ptr<room>;
class room_manager
{
private:
    user_table *_user;   // 一个用户的属性
    online *_onlineuser; // 一个维护用户连接的句柄 里面管理了所有的在线用户
    std::mutex _mutex;
    uint64_t _room_id;
    std::unordered_map<uint64_t, room_ptr> _rid_roomptr;
    std::unordered_map<uint64_t, uint64_t> _uid_rid;

public:
    room_manager(user_table *utb, online *our)
        : _user(utb), _onlineuser(our), _room_id(1)
    {
        DBG_LOG("房间管理模块初始化完毕！");
    }
    ~room_manager()
    {
        DBG_LOG("房间管理模块即将销毁！");
    }

    // 1.匹配成功创建我我们的房间
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 首先确定两者在线
        if (_onlineuser->is_in_game_hall(uid1) == false)
        {
            DBG_LOG("用户：%lu 不在大厅中，创建房间失败!", uid1);
            return room_ptr();
        }
        if (_onlineuser->is_in_game_hall(uid2) == false)
        {
            DBG_LOG("用户：%lu 不在大厅中，创建房间失败!", uid2);
            return room_ptr(); // 空
        }

        std::unique_lock<std::mutex> lock(_mutex);
        {
            room_ptr room_info(new room(_room_id, _user, _onlineuser));

            room_info->set_whiteuser_id(uid1);
            room_info->set_blackuser_id(uid2);
            room_info->set_room_id(_room_id);
            

            _uid_rid.insert(std::make_pair(uid1, _room_id));
            _uid_rid.insert(std::make_pair(uid2, _room_id));
            _rid_roomptr.insert(std::make_pair(_room_id, room_info));
            _room_id++;
            return room_info;
        }
    }
    /*通过房间号获得房间指针*/
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto pos = _rid_roomptr.find(rid);
        if (pos == _rid_roomptr.end())
        {
            return room_ptr();
        }
        return pos->second;
    }
    /*通过用户ID获取房间信息*/
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto tem = _uid_rid.find(uid);
        if (tem == _uid_rid.end())
        {
            return room_ptr();
        }
        auto pos = _rid_roomptr.find(tem->second);
        if (pos == _rid_roomptr.end())
        {
            return room_ptr();
        }
        return pos->second;
    }
    uint64_t get_room_id()
    {
        return _room_id;
    }
    /*通过房间ID销毁房间*/
    void remove_room(uint64_t rid)
    {
        // 删除我们的房间之前要先释放房间内的用户
        room_ptr roominfor = get_room_by_rid(rid); /*该函数加锁了因此线程安全*/
        if (roominfor.get() == nullptr)
        {
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        {
            uint64_t uid1 = roominfor->get_whiteuser_id();
            uint64_t uid2 = roominfor->get_blackuser_id();
            roominfor->handle_exit(uid1);
            roominfor->handle_exit(uid2);
            _uid_rid.erase(uid1);
            _uid_rid.erase(uid2);
            // 当我们的room_ptr的引用计数为0就会自动释放 此时存room信息的就是_rid_roomptr容器 当删除后
            // 引用计数为0就会自动调用room类的析构函数
            _rid_roomptr.erase(rid);
        }
        return;
    }

    /*删除房间中指定用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用*/
    void remove_room_user(uint64_t uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 处理房间中玩家退出动作
        rp->handle_exit(uid);
        // 房间中没有玩家了，则销毁房间
        if (rp->player_count() == 0)
        {
            remove_room(rp->get_room_id());
        }
        return;
    }
};
#endif